package ru.evreke.service

import ActualPlayer
import BoardPayload
import Game
import GamePayload
import GenericServerMessage
import JoinMessage
import PlaceShipMessage
import ServerMessage
import ShotMessage
import io.ktor.server.websocket.*
import org.koin.core.context.GlobalContext
import org.slf4j.LoggerFactory
import placeShipsForDemo
import ru.evreke.service.game.active.ActiveGameService
import ru.evreke.service.game.pending.PendingGamesService
import ru.evreke.service.socket.WebsocketSessionService
import java.util.*

class MessageHandlerService {

    private val activeGameService by GlobalContext.get().inject<ActiveGameService>()
    private val pendingGamesService by GlobalContext.get().inject<PendingGamesService>()
    private val websocketSessionService by GlobalContext.get().inject<WebsocketSessionService>()
    private val logger = LoggerFactory.getLogger("MessageHandlerService")

    fun handleInitMessage(
        playerUUID: String,
    ): ServerMessage {
        val game = Game(UUID.randomUUID().toString(), ActualPlayer(playerUUID))
        logger.debug("Games with id ${game.id} created")

        pendingGamesService.addPendingGame(game.id, game)
        return GenericServerMessage(BoardPayload(game.getUpdatedBoards(playerUUID), game.id))
    }

    fun handleJoinMessage(): ServerMessage = GenericServerMessage(
        GamePayload(
            pendingGamesService.getPendingGames()
        )
    )

    suspend fun handleShotMessage(
        message: ShotMessage
    ) {
        logger.debug("Shot with coords {}", message.coordinates)
        activeGameService.getActiveGame(message.gameId)?.also { game ->
            game.handleShot(message.coordinates)
            sendUpdatedBoardToPlayers(game)
        } ?: logger.error("Game with id=${message.gameId} was not found")
    }

    suspend fun handleJoinMessage(
        message: JoinMessage,
        playerUUID: String,
    ) {
        pendingGamesService.getPendingGame(message.gameId)?.also { game ->
            game.joinPlayer(
                ActualPlayer(playerUUID),
                game.initBoard().also { it.placeShipsForDemo() }
            )

            activeGameService.addActiveGame(message.gameId, game)
            pendingGamesService.removePendingGame(message.gameId)

            // If player is present send to its socket updated boards
            sendUpdatedBoardToPlayers(game)
        } ?: logger.debug("Game ${message.gameId} was not found or already started")
    }

    fun handlePlaceShip(
        message: PlaceShipMessage,
        playerUUID: String,
    ) {
        activeGameService.getActiveGame(message.gameId)?.also { game ->
            game.getBoardByPlayerId(playerUUID).also { board ->
                board.placeShip(message.shipHeadCoordinates, message.ship)
            }
        }
    }

    private suspend fun sendUpdatedBoardToPlayers(game: Game) {
        game.players.forEach { player ->
            websocketSessionService.getPlayerWsSession(player.playerId)?.sendSerialized<ServerMessage>(
                GenericServerMessage(BoardPayload(game.getUpdatedBoards(player.playerId), game.id))
            ) ?: logger.error("Session for player ${player.playerId} was not found")
        }
    }
}